[TECH-278] Cron, Drush and per-job monitoring with an admin overview - #6
Open
OleksandrRiumshyn wants to merge 7 commits into
Open
[TECH-278] Cron, Drush and per-job monitoring with an admin overview#6OleksandrRiumshyn wants to merge 7 commits into
OleksandrRiumshyn wants to merge 7 commits into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds cron and Drush health endpoints, an admin overview page, and per-job Ultimate Cron detection — and closes the gap the logs endpoint cannot see: a check dying silently, with no error keyword written anywhere.
Why
/admin/reports/monitoring/logsmatches error text. That catches a noisy failure and nothing else — if cron is disabled or Drush stops working, nothing is written, the old lines stay clean, and the endpoint reports 200 forever. The new checks answer "when did this last succeed?" instead, which is a question silence can fail.New features
Cron endpoint —
/admin/reports/monitoring/cron. Comparessystem.cron_lastagainst a configurable max age. Core sets that timestamp only after handlers and queues finish, so a run killed part way through never advances it.Drush heartbeat —
/admin/reports/monitoring/drush, plus adrush logs-monitoring:heartbeatcommand and an optional wrapper script. A web request cannot test CLI Drush, so the check is inverted: the CLI bootstraps Drupal, runs a real database query and an entity query, and only then records a timestamp. The write is last on purpose — any failure above it leaves the old timestamp and the endpoint goes stale. A dead man's switch, not a report that can itself go missing.Overview page —
/admin/reports/monitoringshows the live result of all three checks, the per-log breakdown, failing cron jobs and the CLI diagnostics from the last heartbeat. Answers "is the alert right?" without waiting for a poll. Lists each endpoint as an absolute link ready to paste into a monitor, and has a Settings tab.Status report rows — all three checks appear on
/admin/reports/status, including the one thing an endpoint cannot express: a heartbeat that was never recorded because Drush 12.5 is missing from the codebase looks identical to one that is broken.Ultimate Cron submodule —
logs_monitoring_ultimate_cron. Ultimate Cron runs each job independently and swallows per-job failures by design, so one broken job leavessystem.cron_lastlooking perfectly healthy. The submodule inspects every enabled job and reportsdegradednaming the offenders, using each job's own schedule to judge lateness. It plugs in through two documented hooks (logs_monitoring.api.php), so the main module contains no reference to Ultimate Cron at all.Improvements to the existing logs endpoint
filemtime()the endpoint already read but never decided anything with, so a scheduled writer that stops running is caught even though it writes no error text.^10.3 || ^11; Drupal 8/9 are EOL and the promoted properties and Drush attributes here never worked on them.Behaviour changes to be aware of
max_ageis required per log, defaulting to an hour. For a log only written when something breaks, where silence is healthy, set a long value (31536000) to keep the error-word search without the staleness check./admin/reports/monitoring/. The old/admin/reports/logs-monitoringstill works and returns the identical response, since it is already configured in monitors. Deliberately not a redirect: whether a 3xx is followed is the monitor's choice, and an unfollowed one reads as up.Deploy step — please don't skip
The update hooks create
rest.resource.logs_monitoring_{cron,drush}and grant role permissions. On a config-managed site the nextdrush config:importdeletes anything absent from the sync directory, which silently removes the endpoints and leaves the monitor alerting with no visible cause.After
updb, rundrush config:exportand commit the tworest.resource.*files together withuser.role.anonymousanduser.role.authenticated.Fixes found by running this on a real site
hook_requirementswould have crashed/admin/reports/statuson Drupal 11.3+. TheREQUIREMENT_*constants were replaced by aRequirementSeverityenum in 11.3 and removed in 11.4, so naming them raises "Undefined constant" — fatal under PHP 8. Now resolved at runtime, so 10.3 and 11.4 both work.Undefined property: DatabaseLogger::$name. Ultimate Cron's ownCronJob::loadLatestLogEntries()reads a property the Drupal 8+ logger plugins do not have. A warning is not a\Throwable, so a try/catch cannot make it safe — it fills the log and returns a wrong answer. Jobs are now grouped by logger and dispatched to the working instance method.sh drush-healthcheck.shreportedBad substitution. Running it withshbypasses the shebang; it now re-execs under bash rather than half-running.--drushpath failed withtimeout: no such file. The binary was validated against the caller's directory, then the scriptcds into the Drupal root. It is now resolved to absolute first, and the failure message names the binary and the directory.Testing
Run against a Drupal 11.4 site with the module and Ultimate Cron installed:
Coverage targets the contract that matters rather than the happy path: each check returns the right code for
ok/stale/never/degraded, a missing log cannot mask an error found in another, the verdict is never served from a cache, host diagnostics are withheld from anonymous, the superseded logs path returns byte-identical output, and the Ultimate Cron inspection raises no PHP warning.Steps to test
/admin/reports/monitoring/cronreturns 200, thendrush sset system.cron_last 1→ 503staledrush state:delete logs_monitoring.drush_last→/admin/reports/monitoring/drushreturns 503never;drush logs-monitoring:heartbeat→ 200ok/admin/reports/monitoringis 403 anonymously/admin/reports/logs-monitoringstill answers/admin/reports/statuslists the cron, Drush and log rowslogs_monitoring_ultimate_cronenabled, a failing job shows on the overview page and turns the cron endpoint 503Known deprecations, left deliberately
@RestResourceannotations and the proceduralhook_requirementsboth warn on Drupal 11.3+ and are removed in 13. Their replacements do not exist on the^10.3floor, so adopting them now would break the declared range. Note that#[LegacyRequirementsHook]is not the fix — it suppresses the legacy hook, and without an OOP replacement it silently empties the status report.Not covered
The wrapper script's multisite path is exercised only by hand; there is no automated test spanning several sites.